package ro.flashbite.game_engine
{
	import com.greensock.TweenLite;
	import com.greensock.TweenMax;
	
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageQuality;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.system.Capabilities;
	import flash.system.System;
	import flash.ui.Multitouch;
	import flash.ui.MultitouchInputMode;
	import flash.utils.clearTimeout;
	import flash.utils.setTimeout;
	
	import ro.flashbite.console.Console;
	import ro.flashbite.console.ConsoleEvent;
	import ro.flashbite.errors.ArgumentIncompatibleError;
	import ro.flashbite.game_engine.consts.AppInfoConsts;
	import ro.flashbite.game_engine.consts.StatesApplication;
	import ro.flashbite.game_engine.view.LoadingScreen;
	import ro.flashbite.helpers.MovieClipHelpers;
	import ro.flashbite.loggers.ILogger;
	import ro.flashbite.loggers.LoggerFactory;
	import ro.flashbite.loggers.LoggerLevels;
	import ro.flashbite.loggers.targets.ConsoleLoggingTarget;
	import ro.flashbite.loggers.targets.TraceLoggingTarget;
	import ro.flashbite.utils.framer.FrameBeacon;
	
	public class MainGame extends Sprite
	{
		private static const DEFAULT_FRAME_RATE_IN_SLEEP_MODE	:	int = -1;
		private static const DEFAULT_FRAME_RATE_IN_ACTIVE_MODE	:	int = 10;
		
		/**
		 * all loading modules are stored here 
		 */		
		private var _assetsXML:XML;
		/**
		 * on debug mode, when pressing sleep/wakeup on console, the global frame beacon will not stop 
		 */		
		private var _debugMode:Boolean;
		/**
		 * default application FPS 
		 */		
		private var _appFPS:uint;
		/**
		 * start module to load at init time 
		 */		
		public static const INIT_MODULE_ID:String = "start";
		/**
		 * holds all menu classes and id's
		 * 
		 * ex: MENU_LIST = [
		 * 						{classID:"0_0", className:MainMenu, 			type:MenuTypes.MAIN_MENU},
		 *											
		 *											
		 *						{classID:"1_0", className:SinglePlayerMenu1, 	type:MenuTypes.SUB_MENU},
		 *						{classID:"1_1", className:SinglePlayerMenu2, 	type:MenuTypes.SUB_MENU},
		 *						
		 *						
		 *						{classID:"2_0", className:VersusMenu, 			type:MenuTypes.SUB_MENU},
		 *						
		 *						
		 *						{classID:"3_0", className:PauseMenu, 			type:MenuTypes.PAUSE_MENU},
		 *						{classID:"3_1", className:CompleteGameMenu, 	type:MenuTypes.COMPLETE_GAME_MENU},
		 *						{classID:"3_2", className:GameOverMenu, 		type:MenuTypes.GAME_OVER_MENU},
		 *						
		 *						
		 *						{classID:"4_0", className:HighScoresMenu, 		type:MenuTypes.SUB_MENU},
		 *						
		 *						
		 *						{classID:"5_0", className:AboutMenu, 			type:MenuTypes.SUB_MENU},
		 *						
		 *						
		 *						{classID:"6_0", className:HowToPlayMenu, 		type:MenuTypes.SUB_MENU}
		 * 					];
		 */		
		private var _menuList:Array;
		/**
		 * holds all menu classes and id's
		 * 
		 * ex: LEVEL_LIST = [
		 * 						{classID:"1_0", className:SinglePlayerLevel},
		 * 						{classID:"1_1", className:SinglePlayerLevel},
		 * 
		 * 						{classID:"2_0", className:MultiPlayerAgainstLevel},
		 * 						{classID:"2_1", className:MultiPlayerAgainstLevel},
		 * 
		 * 						{classID:"3_0", className:MultiPlayerCoopLevel},
		 * 						{classID:"3_1", className:MultiPlayerCoopLevel}
		 * 					]
		 */		
		private var _levelList:Array;
		/**
		 * last known dimensions of stage
		 */		
		private var _lastStageWidth:uint;
		private var _lastStageHeight:uint;
		/**
		 * tells if is touch supported on device 
		 */		
		private var _isTouchSupported:Boolean;
		/**
		 * holds the current state of the application 
		 */		
		private var _currentAppState:uint;
		/**
		 * console for debug management 
		 */		
		private var _console:Console;
		/**
		 * application game engine 
		 */		
		private var _engine:GameEngine;
		/**
		 * logger for debug purpose 
		 */		
		private var _logger:ILogger;
		/**
		 * holds all running tweens to pause and resume on major device events 
		 */		
		private var _runningTweens:Vector.<TweenLite>;
		
		/**
		 * this will un-pause game; on returning to app, there is a delay between Event.ACTIVATE and the real app is in full state
		 */		
		private static const TIMER_UNPAUSE	:	uint = 300; //ms
		private var _timerID:uint;
		
		// ================================================================================================================================
		// CONSTRUCTOR
		// ================================================================================================================================
		
		public function MainGame(appID:String, 
								 loadingScreen:LoadingScreen, 
								 assetsXML:XML, 
								 menuList:Array, 
								 levelList:Array, 
								 debugMode:Boolean = false, 
								 appFPS:uint = 30)
		{
			_constructor(appID, loadingScreen, assetsXML, menuList, levelList, debugMode, appFPS);
		}
		private function _constructor(appID:String, 
									  loadingScreen:LoadingScreen,
									  assetsXML:XML, 
									  menuList:Array, 
									  levelList:Array, 
									  debugMode:Boolean, 
									  appFPS:uint):void
		{
			//thown errors
			if (appID == null) throw new ArgumentIncompatibleError("appID");
			//if (loadingScreen == null) throw new ArgumentIncompatibleError("loadingScreen"); //not important at thsi moment
			if (assetsXML == null) throw new ArgumentIncompatibleError("assetsXML");
			if (menuList == null) throw new ArgumentIncompatibleError("menuList");
			if (levelList == null) throw new ArgumentIncompatibleError("levelList");
			
			//set current state of application
			_currentAppState = StatesApplication.INITIALIZING;
			
			//save data
			_assetsXML = assetsXML;
			_menuList = menuList;
			_levelList = levelList;
			_debugMode = debugMode;
			_appFPS = appFPS;
			
			//stage preps
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.quality = StageQuality.LOW;
			//orientation left
			_lastStageWidth = Capabilities.screenResolutionY;
			_lastStageHeight = Capabilities.screenResolutionX;
			stage.stageWidth = _lastStageWidth;
			stage.stageHeight = _lastStageHeight;
			//default frame rate
			this.stage.frameRate = DEFAULT_FRAME_RATE_IN_ACTIVE_MODE;
			
			//get touch support prop
			_isTouchSupported = Multitouch.supportsTouchEvents;
			//set support for console -> Touch Point
			if (_isTouchSupported) {
				Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
			}
			
			//Loggers and Console
			if (_debugMode) {
				//init output level
				LoggerFactory.initialize(LoggerLevels.ALL);
				//FrameBeacon
				(FrameBeacon.getInstance(_appFPS, true) as FrameBeacon).start();
				//init console
				_console = new Console(this.stage, _levelList);
				_console.addEventListener(ConsoleEvent.SLEEP, 				onConsoleEvent, false, 0, true);
				_console.addEventListener(ConsoleEvent.WAKEUP, 				onConsoleEvent, false, 0, true);
				_console.addEventListener(ConsoleEvent.DESTROY, 		 	onConsoleEvent, false, 0, true);
				_console.addEventListener(ConsoleEvent.COMBO_LEVEL_CLICK, 	onConsoleEvent, false, 0, true);
				//add output targets to LoggerFactory
				LoggerFactory.addTarget(new TraceLoggingTarget());
				LoggerFactory.addTarget(new ConsoleLoggingTarget(_console as Console));
				//start console
				_console.start();
			} else {
				//init output level
				LoggerFactory.initialize(LoggerLevels.NO_OUTPUT);
				//FrameBeacon
				(FrameBeacon.getInstance(_appFPS, true) as FrameBeacon).start();
			}
			_logger = LoggerFactory.getLogger("MainGame");
			
			//resize management
			stage.addEventListener(Event.RESIZE, onStageResize, false, 0, true);
			
			//major events in application
			stage.addEventListener(Event.ACTIVATE, 	 onDeviceEvent, false, 0, true);
			stage.addEventListener(Event.DEACTIVATE, onDeviceEvent, false, 0, true);
			
			//create game engine and start it
			_engine = new GameEngine(this, appID, loadingScreen, assetsXML, INIT_MODULE_ID, menuList, levelList, _console);
			_engine.start();
			
			_currentAppState = StatesApplication.ACTIVE;
		}
		
		// ================================================================================================================================
		// ISleepWakeupDestroy
		// ================================================================================================================================
		
		private function sleep():void
		{
			switch (_currentAppState) {
				case StatesApplication.ACTIVE:
					_logger.debug("APPLICATION SLEEP");
					//sleep engine
					if (_engine) {
						_engine.sleep();
					}
					//sleep FrameBeacon and set frameRate to non-redrawable
					if (_debugMode == false) {
						(FrameBeacon.getInstance() as FrameBeacon).pause();
						this.stage.frameRate = DEFAULT_FRAME_RATE_IN_SLEEP_MODE;
					}
					//sleep tweens
					//TweenMax.pauseAll();
					sleepActiveTweens();
					//problem on tween max timeScale
					TweenMax.globalTimeScale = .0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001;
					break;
			}
			
			_currentAppState = StatesApplication.INACTIVE;
		}
		
		private function wakeup():void
		{
			switch (_currentAppState) {
				case StatesApplication.INACTIVE:
					_logger.debug("APPLICATION WAKEUP");
					//wakeup engine
					if (_engine) {
						_engine.wakeup();
					}
					
					if (_debugMode == false) {
						(FrameBeacon.getInstance() as FrameBeacon).unpause();
						this.stage.frameRate = DEFAULT_FRAME_RATE_IN_ACTIVE_MODE;
					}
					
					//resume paused tweens
					TweenMax.globalTimeScale = 1;
					//TweenMax.resumeAll();
					if (_runningTweens && _runningTweens.length) {
						for each (var sleepedTween:TweenLite in _runningTweens) {
							sleepedTween.resume();
						}
					}
					break;
			}
			
			_currentAppState = StatesApplication.ACTIVE;
		}
		
		protected function destroy():void
		{
			if (_currentAppState == StatesApplication.DESTROYED) {
				System.gc();
				return;
			}
			
			_logger.debug("DESTROY");
			
			//set state as destroyed
			_currentAppState = StatesApplication.DESTROYED; 
			//destroy engine
			if (_engine) {
				_engine.destroy();
				_engine = null;
			}
			
			_runningTweens = null;
			
			//kill all tweens
			TweenMax.killAll();
			TweenMax.destroy();
			//clear this of children
			MovieClipHelpers.removeAllChildren(this);
			//clean-up stage listeners
			stage.removeEventListener(Event.RESIZE, 	onStageResize);
			stage.removeEventListener(Event.ACTIVATE, 	onDeviceEvent);
			stage.removeEventListener(Event.DEACTIVATE, onDeviceEvent);
			
			//destroy logger
			if (_logger) {
				LoggerFactory.clearLogger(_logger.name);
				_logger = null;
			}
			
			//console stays + FrameBeacon if is in debug mode
			if (_debugMode == false) {
				//destroy console
				if (_console) {
					_console.removeEventListener(ConsoleEvent.SLEEP, 			 onConsoleEvent);
					_console.removeEventListener(ConsoleEvent.WAKEUP, 			 onConsoleEvent);
					_console.removeEventListener(ConsoleEvent.DESTROY, 			 onConsoleEvent);
					_console.removeEventListener(ConsoleEvent.COMBO_LEVEL_CLICK, onConsoleEvent);
					_console.destroy();
					_console = null;
				}
				//destroy beacons
				(FrameBeacon.getInstance() as FrameBeacon).destroy();
				//destroy logger factory
				LoggerFactory.destroy();
			}
			
			System.gc();
		}
		
		// ================================================================================================================================
		// PRIVATE
		// ================================================================================================================================
		
		private function sleepActiveTweens():void
		{
			_runningTweens = new Vector.<TweenLite>();
			var activeTweens:Array = TweenMax.getAllTweens();
			for each (var tween:TweenLite in activeTweens) {
				if (tween.active && tween.paused == false) {
					tween.pause();
					_runningTweens.push(tween);
				}
			}
		}
		
		private function onStageResize(e:Event):void
		{
			var currentStageWidth:uint = stage.stageWidth,
				currentStageHeight:uint = stage.stageHeight;
			//no need for update if the same width and height
			if (stage.stageWidth == _lastStageWidth && stage.stageHeight == _lastStageHeight) return;
			//console resize
			if (_console) {
				(_console as Console).resize(currentStageWidth, currentStageHeight);
			}
			//save last used dimensions
			_lastStageWidth = currentStageWidth;
			_lastStageHeight = currentStageHeight;
			//invalidate stage to redraw all
			stage.invalidate();
		}
		
		private function onConsoleEvent(e:ConsoleEvent):void
		{
			switch (e.type) {
				case ConsoleEvent.SLEEP:
					sleep();
					break;
				case ConsoleEvent.WAKEUP:
					wakeup();
					break;
				case ConsoleEvent.DESTROY:
					destroy();
					break;
				case ConsoleEvent.COMBO_LEVEL_CLICK:
					//save difficulty as 1
					_engine.saveSharedData(AppInfoConsts.CURRENT_DIFFICULTY_LEVEL, 1, true);
					//save character as the first one "char1"
					_engine.saveSharedData(AppInfoConsts.CURRENT_CHARACTER_ID, "char1", true);
					//save level as the first one
					_engine.saveSharedData(AppInfoConsts.CURRENT_LEVEL_ID, e.data as String, true);
					
					//go load selected level
					_engine.loadLevel();
					break;
			}
		}
		
		private function onDeviceEvent(e:Event):void
		{
			switch (e.type) {
				case Event.ACTIVATE:
					var timeRealWakeup:uint = TIMER_UNPAUSE;
					if (_debugMode) {
						timeRealWakeup = 0;
					} 
					clearTimeout(_timerID);
					setTimeout(wakeup, timeRealWakeup);
					break;
				case Event.DEACTIVATE:
					clearTimeout(_timerID);
					sleep();
					System.gc();
					break;
			}
		}
	}
}